package ac.svn.accesscontrol.controller;

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import ac.notification.event.INotificationListener;
import ac.notification.event.NotificationEvent;
import ac.notification.manager.NotificationRegistrar;
import ac.svn.accesscontrol.manager.SVNInfoManager;
import ac.svn.accesscontrol.store.AccessUnit;
import ac.svn.accesscontrol.store.Dictionary;
import ac.svn.accesscontrol.store.IResources;
import ac.svn.accesscontrol.store.ISVNIdentifier;
import ac.svn.accesscontrol.store.ISVNItem;
import ac.svn.accesscontrol.store.SVNGroup;
import ac.svn.accesscontrol.store.SVNLocation;
import ac.svn.accesscontrol.store.SVNUser;
import ac.svn.accesscontrol.store.UserDetails;
import ac.svn.accesscontrol.ui.IUIConstants;
import ac.svn.accesscontrol.ui.UIComponentLibrary;
import ac.svn.accesscontrol.ui.helper.ContentLister;
import ac.svn.accesscontrol.ui.helper.formatter.ItemTypeTextFormatter;
import ac.svn.accesscontrol.ui.model.ThreadedListModel;
import ac.svn.accesscontrol.ui.renderer.SimpleIconRenderer;
import ac.svn.accesscontrol.ui.window.IdentifierChangerDialog;
import ac.svn.accesscontrol.utils.Utility;

public class IdentifierChangeController implements INotificationListener,
ISVNIdentifier , IUIConstants
{

	private ISVNItem targetItem;
	private IdentifierChangerDialog idChangerDialog;
	private ContentLister contentLister;
	private ThreadedListModel threadedListModel;
	private JMenuItem changeIdentifierMI;


	public IdentifierChangeController()
	{
		targetItem = null;
		idChangerDialog = new IdentifierChangerDialog();

		threadedListModel = new ThreadedListModel();
		idChangerDialog.getItemList().setModel(threadedListModel);

		SimpleIconRenderer iconRenderer =new SimpleIconRenderer(IResources.LOC_ICN_URL);
		iconRenderer.setTextFormatter(new ItemTypeTextFormatter());
		idChangerDialog.getItemList().setCellRenderer(iconRenderer);

		contentLister = new ContentLister(idChangerDialog.getItemList(), threadedListModel, null);
		contentLister.setListClrRequired(true);
		ListingController.getListingController().registerListerForNotifications(contentLister, IUIConstants.C_LIST_TYPE_GROUP);
		ListingController.getListingController().registerListerForNotifications(contentLister, IUIConstants.C_LIST_TYPE_USER);
		ListingController.getListingController().registerListerForNotifications(contentLister, IUIConstants.C_LIST_TYPE_LOCATION);

		JMenu menu = (JMenu) UIComponentLibrary.getComponent(M_TOOL_MENU);

		changeIdentifierMI = new JMenuItem("Change Identifiers");
		changeIdentifierMI.setIcon(Utility.getImageIcon(IResources.LOC_ICN_ID_SMALL));
		menu.add(changeIdentifierMI);

		//Activate UI.
		registerListeners();

		//Start the operations.
		NotificationRegistrar.registerListener(this);
	}


	private void registerListeners() 
	{
		JList itemList =idChangerDialog.getItemList();
		itemList.addListSelectionListener(new ListSelectionListener() {

			@Override
			public void valueChanged(ListSelectionEvent lse) {
				if(false == lse.getValueIsAdjusting())
				{
					targetItem = (ISVNItem) idChangerDialog.getItemList().getSelectedValue();
					if(null != targetItem)
					{
						loadInformation(targetItem);
					}
				}
			}
		});

		itemList.addMouseListener(new MouseAdapter() {

			/* (non-Javadoc)
			 * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
			 */
			@Override
			public void mouseClicked(MouseEvent arg0) {
				targetItem = (ISVNItem) idChangerDialog.getItemList().getSelectedValue();
				if(null != targetItem)
				{
					loadInformation(targetItem);
				}
			}			
		});

		idChangerDialog.getApplyButton().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				applyHandler();
			}
		});

		idChangerDialog.getItemIdTxt().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				applyHandler();
			}
		});

		ActionListener reloadListener = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				resetUI();
				reloadLists(idChangerDialog.getSearchField().getText());			
			}
		};

		idChangerDialog.getUserCheckBox().addActionListener(reloadListener);
		idChangerDialog.getGroupCheckBox().addActionListener(reloadListener);
		idChangerDialog.getLocationCheckBox().addActionListener(reloadListener);

		changeIdentifierMI.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				openDialogHandler();
			}
		});

		idChangerDialog.getSearchField().addKeyListener(new KeyAdapter() 
		{
			@Override
			public void keyTyped(KeyEvent e) 
			{
				reloadLists(idChangerDialog.getSearchField().getText());
			}
		});

	}

	protected void openDialogHandler() 
	{
		SwingUtilities.invokeLater(new Runnable() 
		{
			@Override
			public void run() 
			{
				resetUI();
				idChangerDialog.setVisible(true);
			}
		});		
	}


	protected void resetUI() 
	{
		idChangerDialog.getItemIdTxt().setText("");
		idChangerDialog.getItemList().clearSelection();
		idChangerDialog.getDetialsTextArea().setText("");
	}


	protected void reloadLists(String criteria) 
	{
		//Create final list of items to be displayed.
		Set<ISVNItem> finalList = new HashSet<ISVNItem>();

		if(idChangerDialog.getUserCheckBox().isSelected())
		{
			if(null == criteria)
			{
				finalList.addAll(SVNInfoManager.getInfoManager().getAllUsers());
			}
			else
			{
				for(ISVNItem item : SVNInfoManager.getInfoManager().getAllUsers())
				{
					if(item.getMetaData().toLowerCase().contains(criteria))
					{
						finalList.add(item);
					}
				}
			}			
		}

		if(idChangerDialog.getGroupCheckBox().isSelected())
		{
			if(null == criteria)
			{
				finalList.addAll(SVNInfoManager.getInfoManager().getAllGroups());
			}
			else
			{
				for(ISVNItem item : SVNInfoManager.getInfoManager().getAllGroups())
				{
					if(item.getMetaData().toLowerCase().contains(criteria.toLowerCase()))
					{
						finalList.add(item);
					}
				}
			}	
		}

		if(idChangerDialog.getLocationCheckBox().isSelected())
		{
			if(null == criteria)
			{
				finalList.addAll(SVNInfoManager.getInfoManager().getAllLocations());
			}
			else
			{
				for(ISVNItem item : SVNInfoManager.getInfoManager().getAllLocations())
				{
					if(item.getMetaData().toLowerCase().contains(criteria.toLowerCase()))
					{
						finalList.add(item);
					}
				}
			}	
		}

		contentLister.listItems(finalList);
	}


	protected void applyHandler() 
	{
		boolean processSuccessful = false;

		//Check we have made any selection on Item's List.
		if(null != targetItem)
		{

			final String newIdString = idChangerDialog.getItemIdTxt().getText().trim();
			SVNInfoManager infoManager = SVNInfoManager.getInfoManager();

			//Check the type of selected Item.
			if(targetItem instanceof SVNUser)
			{
				//User's identifier is changed.
				SVNUser svnUser = (SVNUser) targetItem;

				idChangerDialog.addProgressText("Calculating changes...", true);			

				//Check if identifier is changed at  all.
				if(false == svnUser.getUserAlias().equalsIgnoreCase(newIdString))
				{
					//Check if new user id is duplicated.
					SVNUser existentUser = infoManager.getSvnUser(newIdString);

					idChangerDialog.addProgressText("Checking conflicting identifiers..", false);

					//If not such user exist.
					if(null == existentUser)
					{
						idChangerDialog.addProgressText("Updating Groups..", false);

						//For all id listed in user's object get reference to actual object.
						for(String groupId : svnUser.getGroups())
						{
							SVNGroup group = infoManager.getGroup(groupId);

							//Update member ship
							group.getGroupMembers().remove(svnUser.getUserAlias());

							//Add new User Id
							group.addMember(newIdString);

							//Update Group Back on Information Manager.
							infoManager.updateGroup(group);

						}

						idChangerDialog.addProgressText("Updating Access rights..", false);

						//Get all access Unit in which user is involved.
						Set<ISVNItem> accessUnits = infoManager.getAllAccessUnit(svnUser.getUserAlias());

						//For all these update the name of owner.
						for(ISVNItem item : accessUnits)
						{
							AccessUnit accessUnit = (AccessUnit) item;

							//Update new ID.
							accessUnit.setOwnerId(newIdString);

							//Update access Unit back on Information Manager.
							infoManager.updateAccessUnit(accessUnit);
						}

						idChangerDialog.addProgressText("Updating Contact information..", false);

						//Update dictionary where additional user information is kept.
						Dictionary dictionary = Dictionary.getDictionary();
						UserDetails userDetails = dictionary.getUserDetails(svnUser.getUserAlias());

						//If additional information is saved for user
						if(null != userDetails)
						{
							userDetails.setUserId(newIdString);
							//Update details back to dictionary.
							dictionary.updateUserDetails(userDetails);
						}

						//remove old user object.
						infoManager.removeUser(svnUser.getId());
						
						idChangerDialog.addProgressText("Updating User..", false);
						//Update user itself
						svnUser.setUserAlias(newIdString);

						infoManager.updateUser(svnUser);

						idChangerDialog.addProgressText("Update Successful!", false);
						processSuccessful = true;
					}
					else
					{
						idChangerDialog.addProgressText("Operation aborted!", false);
						String message = "A User exists with same identifier, kindly provide another!";
						JOptionPane.showMessageDialog(idChangerDialog, message,"Duplicate Entry!",JOptionPane.ERROR_MESSAGE);
					}
				}
				else
				{
					idChangerDialog.addProgressText("No change dectected, operation by passed!", false);
				}			
			}				
			else if(targetItem instanceof SVNGroup)
			{
				SVNGroup group = (SVNGroup) targetItem;

				idChangerDialog.addProgressText("Calculating changes...", true);		

				//Check if identifier is changed at  all.
				if(false == group.getGroupAlias().equalsIgnoreCase(newIdString))
				{

					//Check if any group exist with new alias.
					SVNGroup existentGroup = infoManager.getGroup(newIdString);

					idChangerDialog.addProgressText("Checking confilicting identifiers..", false);

					//No such group should exist.
					if(null == existentGroup)
					{
						idChangerDialog.addProgressText("Updating Users..", false);

						//Get List of users who are part of group as information should be updated in them.
						for(String userAlias : group.getGroupMembers())
						{
							SVNUser svnUser = infoManager.getSvnUser(userAlias);

							//Update new Group Id.
							svnUser.getGroups().remove(group.getGroupAlias());
							svnUser.addGroup(newIdString);							

							//Update user back to Information Manager.
							infoManager.updateUser(svnUser);							
						}						

						idChangerDialog.addProgressText("Updating Access rights..", false);

						//Get all access Unit in which user is involved.
						Set<ISVNItem> accessUnits = infoManager.getAllAccessUnit(group.getGroupAlias());

						//For all these update the name of owner.
						for(ISVNItem item : accessUnits)
						{
							AccessUnit accessUnit = (AccessUnit) item;

							//Update new ID.
							accessUnit.setOwnerId(newIdString);

							//Update access Unit back on Information Manager.
							infoManager.updateAccessUnit(accessUnit);
						}

						//Remove old group object.
						infoManager.removeGroup(group.getId());
						
						idChangerDialog.addProgressText("Updating Group..", false);

						//Update new id in group.
						group.setGroupAlias(newIdString);

						//Update group in Information Manager.
						infoManager.updateGroup(group);

						idChangerDialog.addProgressText("Update Successful!", false);

						processSuccessful = true;
					}
					else
					{
						idChangerDialog.addProgressText("Operation aborted!", false);
						String message = "A Group exists with same identifier, kindly provide another!";
						JOptionPane.showMessageDialog(idChangerDialog, message,"Duplicate Entry!",JOptionPane.ERROR_MESSAGE);
					}					
				}
				else
				{
					idChangerDialog.addProgressText("No change dectected, operation by passed!", false);
				}				
			}
			else if(targetItem instanceof SVNLocation)
			{
				SVNLocation location = (SVNLocation) targetItem;

				idChangerDialog.addProgressText("Calculating changes...", true);		

				//Check if identifier is changed at  all.
				if(false == location.getLocationPath().equalsIgnoreCase(newIdString))
				{
					//Get location that might present with new path. 
					SVNLocation existentLoction = infoManager.getLocation(newIdString);

					idChangerDialog.addProgressText("Checking confilicting identifiers..", false);

					//Proceed only if no such location exists.
					if(null == existentLoction)
					{
						idChangerDialog.addProgressText("Updating Access rights..", false);

						// Update location Id in all associated access unit.
						for(String accessUnitId : location.getAccessUnits())
						{
							AccessUnit accessUnit = infoManager.getAccessUnit(accessUnitId);

							//Update path
							accessUnit.setLocationPath(newIdString);

							//Update Access Unit in Information Manager.
							infoManager.updateAccessUnit(accessUnit);
						}

						infoManager.removeLocation(location.getId());
						
						idChangerDialog.addProgressText("Updating Location..", false);

						//Update location itself
						location.setLocationPath(newIdString);

						//Update Location in Information Manager.
						infoManager.updateLocation(location);

						idChangerDialog.addProgressText("Update Successful!", false);

						processSuccessful = true;
					}
					else
					{
						idChangerDialog.addProgressText("Operation aborted!", false);
						String message = "A Location exists with same identifier, kindly provide another!";
						JOptionPane.showMessageDialog(idChangerDialog, message,"Duplicate Entry!",JOptionPane.ERROR_MESSAGE);
					}
				}
				else
				{
					idChangerDialog.addProgressText("No change dectected, operation by passed!", false);
				}
			}
		}
		else
		{
			idChangerDialog.addProgressText("Operation aborted!", false);
			//Show Message
			String message = "Need to select an item from the list before applying the changes!";
			JOptionPane.showMessageDialog(idChangerDialog,message,"No Selection!",JOptionPane.WARNING_MESSAGE);
		}

		//Prevent unnecessary Update for Error conditions.
		if(processSuccessful)
		{
			resetUI();

			Utility.sendNotification(N_UPDATE_LISTINGS,IUIConstants.C_LIST_TYPE_GROUP);
			Utility.sendNotification(N_UPDATE_LISTINGS,IUIConstants.C_LIST_TYPE_LOCATION);
			Utility.sendNotification(N_UPDATE_LISTINGS,IUIConstants.C_LIST_TYPE_ACCESS);
			Utility.sendNotification(N_UPDATE_LISTINGS,IUIConstants.C_LIST_TYPE_USER);

			idChangerDialog.addProgressText("You have updated critical part of SVN accounts. " +
					"It is highly suggest to save Access File and Configuration File to avoid loss of changes!", false);
		}

	}

	protected void loadInformation(ISVNItem selItem) 
	{
		if(null != selItem)
		{
			if(selItem instanceof SVNUser)
			{
				SVNUser svnUser = (SVNUser) selItem;
				String details = String.format("User: %s     Alias: %s\nGroups: %s", svnUser.getUserName(),svnUser.getUserAlias(),svnUser.getGroups());
				idChangerDialog.getDetialsTextArea().setText(details);
				idChangerDialog.getItemIdTxt().setText(svnUser.getUserAlias());				
			}				
			else if(selItem instanceof SVNGroup)
			{
				SVNGroup group = (SVNGroup) selItem;
				String details = String.format("Group: %s     Alias: %s\nMembers: %s", group.getGroupName(),group.getGroupAlias(),group.getGroupMembers());
				idChangerDialog.getDetialsTextArea().setText(details);
				idChangerDialog.getItemIdTxt().setText(group.getGroupAlias());
			}
			else if(selItem instanceof SVNLocation)
			{
				SVNLocation location = (SVNLocation) selItem;
				String details = String.format("Location: %s     \nURL: %s",location.getLocationName(),location.getLocationPath());
				idChangerDialog.getDetialsTextArea().setText(details);
				idChangerDialog.getItemIdTxt().setText(location.getLocationPath());
			}

			idChangerDialog.getDetialsTextArea().setCaretPosition(0);
		}
	}


	@Override
	public void receiveEvent(NotificationEvent notificationEvent) 
	{
		final String neId = notificationEvent.getPropertyIdentifier();
		if(neId.contains(CMD_CHANGE_IDENTIFIER))
		{
			changeIdentifierHandler(notificationEvent);
		}
		else if(neId.contains(N_UPDATE_LISTINGS))
		{
			EventQueue.invokeLater(new Runnable() {
				@Override
				public void run() {
					reloadLists(null);
				}
			});
		}
	}

	private void changeIdentifierHandler(NotificationEvent notificationEvent) 
	{
		final ISVNItem svnItem = (ISVNItem) notificationEvent.getPropertyValue();
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() 
			{
				if(null != svnItem)
				{
					listRelevantItems(svnItem);
					resetUI();
					reloadLists("");
					loadInformation(svnItem);
					idChangerDialog.getItemList().setSelectedValue(svnItem, true);
					idChangerDialog.setVisible(true);
				}

			}

			private void listRelevantItems(ISVNItem svnItem) 
			{
				if(svnItem instanceof SVNUser)
				{
					idChangerDialog.getUserCheckBox().setSelected(true);
				}
				else if(svnItem instanceof SVNGroup)
				{
					idChangerDialog.getGroupCheckBox().setSelected(true);
				}
				else if(svnItem instanceof SVNLocation)
				{
					idChangerDialog.getLocationCheckBox().setSelected(true);
				}

			}
		});

	}

	@Override
	public Set<String> declaredSubscriptionSet() 
	{
		Set<String> subscription = new HashSet<String>();
		subscription.add(CMD_CHANGE_IDENTIFIER);
		subscription.add(N_UPDATE_LISTINGS);
		return subscription;
	}

}
